package com.piece.core.oauth.config;

import com.piece.core.authentication.exception.CustomWebResponseExceptionTranslator;
import com.piece.core.web.authority.token.CustomTokenEnhancer;
import com.piece.core.web.authority.token.TokenServices;
import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.authentication.service.CustomClientDetailsService;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.cache.CacheFactory;
import com.piece.core.framework.properties.ApplicationProperties;
import com.piece.core.web.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

public class CustomAuthorizationServerHandler {

    @Autowired
    private ApplicationProperties applicationProperties;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private CacheFactory cacheFactory;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore tokenStore;

    @Autowired(required = false)
    private List<AuthorizationServerProvider> configurers;

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }

    @Bean("clientDetailsService")
    public CustomClientDetailsService clientDetailsService() {
        CustomClientDetailsService clientDetailsService = new CustomClientDetailsService(dataSource, cacheFactory);
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }

    @Bean("oAuth2RequestFactory")
    public OAuth2RequestFactory oAuth2RequestFactory(ClientDetailsService clientDetailsService) {
        return new DefaultOAuth2RequestFactory(clientDetailsService);
    }

    @Primary
    @Bean("tokenServices")
    public TokenServices tokenServices(TokenEnhancer tokenEnhancer, ClientDetailsService clientDetailsService) {
        boolean kickLogin = Convert.toBool(applicationProperties.getValue(ApplicationConstants.SYSTEM_AUTH_LOGIN_KICK), true);
        TokenServices tokenServices = new TokenServices(kickLogin, securityProperties);
        tokenServices.setAuthenticationManager(authenticationManager);
        tokenServices.setTokenStore(tokenStore);
        tokenServices.setTokenEnhancer(tokenEnhancer);
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setReuseRefreshToken(false);
        tokenServices.setClientDetailsService(clientDetailsService);
        return tokenServices;
    }

    @Bean("tokenGranter")
    public TokenGranter tokenGranter(ClientDetailsService clientDetailsService, TokenServices tokenServices, AuthorizationCodeServices authorizationCodeServices) {
        return new TokenGranter() {
            private CompositeTokenGranter delegate;

            @Override
            public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
                if (null == delegate) {
                    delegate = new CompositeTokenGranter(getAllTokenGranters(clientDetailsService, tokenServices, authorizationCodeServices));
                }
                return delegate.grant(grantType, tokenRequest);
            }
        };
    }

    /**
     * 所有授权模式：默认的5种模式 + 自定义的模式
     */
    private List<TokenGranter> getAllTokenGranters(ClientDetailsService clientDetailsService, TokenServices tokenServices, AuthorizationCodeServices authorizationCodeServices) {
        OAuth2RequestFactory factory = new DefaultOAuth2RequestFactory(clientDetailsService);
        //获取默认的授权模式
        List<TokenGranter> tokenGranters = getDefaultTokenGranters(tokenServices, authorizationCodeServices, factory, clientDetailsService);

        if (null != configurers) {
            for (AuthorizationServerProvider provider : configurers) {
                tokenGranters.addAll(provider.addTokenGranter(authenticationManager, factory, tokenServices, clientDetailsService));
            }
        }

        return tokenGranters;
    }

    /**
     * 默认的授权模式
     */
    private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerTokenServices tokenServices
            , AuthorizationCodeServices authorizationCodeServices
            , OAuth2RequestFactory requestFactory
            , ClientDetailsService clientDetailsService) {
        List<TokenGranter> tokenGranters = new ArrayList<>();
        // 添加授权码模式
        tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory));
        // 添加刷新令牌的模式
        tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
        // 添加隐士授权模式
        tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
        // 添加客户端模式
        tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
        if (null != authenticationManager) {
            // 添加密码模式
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
        }
        return tokenGranters;
    }

    @Bean(name = "webResponseExceptionTranslator")
    public WebResponseExceptionTranslator webResponseExceptionTranslator() {
        return new CustomWebResponseExceptionTranslator();
    }
}
